import { Random } from "../../core/utils";
import { SubjectComponent } from "../../core/subject";
import World from "./world";
import BulletStand from "./bullet_stand";

export const LAND_ATTACH_DISTANCE = 25;

const { executeInEditMode, ccclass, property } = cc._decorator;

export class Contact {
    loc: cc.Vec2; //与碰撞面的接触点。
    normal: cc.Vec2; //垂直碰撞面法线
    constructor(loc, normal) {
        this.loc = loc;
        this.normal = normal;
    }
};

export class BulletHit {
    self: Bullet = null;
    other: Bullet = null;
    contacts: Contact[] = [];
};


export enum BulletState {
    Stand = 1,
    Physics = 2,
    Grabed = 3,
};


export class DamageType {
    key = "";
    value = 0;
};

@ccclass
// @executeInEditMode
export default class Bullet extends SubjectComponent {
    state: BulletState = BulletState.Physics;
    stand: BulletStand = null; //站立对象，记录自己和被站立的bullet
    capsuleBullet: Bullet = null; //被capsule容纳。x,y 都会受到capsule的deltaPos影响。
    capsuleBulletUid = 0;
    hits: BulletHit[] = []; //碰撞

    bulletUid = 0;
    world: World = null;

    @property()
    tag = "";
    @property()
    speed = cc.v2();
    @property()
    speedMax = cc.v2(800, 2000); //可以达到的最大速度
    @property()
    damping = 97; //速度衰减：speed = speed * (damping/100);
    @property()
    gravityScale = 100; //0-100重力因子：重力*gravityScale = 最终的重力影响
    @property
    elastic = 10; //0-99 弹力：碰撞时，速度 * -(elastic/100)
    @property
    friction = 100; //0-100 摩擦力：被站时，bullet移动会带动上面物体的移动百分比。

    @property
    physicsEnabled = true; //开关物理
    @property
    isSolid = false; //是否实体（可以被站立，不能被穿透）
    @property
    isSensor = false; //是否感应器
    @property
    isGrabable = false; //是否可以拿取
    @property
    isUseable = false; //是否可以使用

    @property({
        serializable: true
    })
    _z = 1; //范围0-99
    @property
    get z() {
        return this._z;
    }
    set z(val) {
        this._z = val;
        this.node.zIndex = this._z;
    }

    @property(Bullet)
    staticCapsuleBy: Bullet = null; //todo: 编辑器中指定capsule

    constructor() {
        super();
        this.bulletUid = Random.range(10000, 99999);
    }
    protected onDestroy(): void {
        this.emit("destroy");
        super.onDestroy && super.onDestroy();
    }

    setCapsule(bullet: Bullet) {
        if (this.capsuleBullet) {
            this.capsuleBullet = null;
            this.capsuleBulletUid = 0;
        }

        this.capsuleBullet = bullet;
        if (this.capsuleBullet) {
            this.capsuleBulletUid = this.capsuleBullet.bulletUid;
        }
    }

    get curPos() {
        return cc.v2(this.node.position);
    }
    set curPos(val) {
        this.node.setPosition(val);
    }
    prevPos: cc.Vec2 = null;
    deltaPos: cc.Vec2 = cc.v2();
    protected onLoad(): void {
        super.onLoad && super.onLoad();
        this.world = this.node.parent.getComponent(World);

        if (this.staticCapsuleBy) {
            this.setCapsule(this.staticCapsuleBy);
        }

        if (this.z !== this.node.zIndex) {
            this.node.zIndex = this.z;
        }
    }

    updateStand(dt: number, hit: BulletHit) {
        return false;
    }
    updateSpeed(dt: number) {
    }
    updateCapsule(dt: number) {
        if (cc.isValid(this.capsuleBullet)) {
            this.node.setPosition(cc.v2(this.node.position).add(this.capsuleBullet.deltaPos));
        }
    }
    protected update(dt: number): void {
        if (!this.physicsEnabled) {
            return;
        }

        for (let i = 0; i < this.hits.length;) {
            let hit = this.hits[i];
            if (!cc.isValid(hit.other)) {
                this.hits.splice(i, 1);
                continue;
            }
            this.updateStand(dt, hit);
            i++;
        }

        this.updateSpeed(dt);

        if (cc.isValid(this) && this.state === BulletState.Stand && cc.isValid(this.stand) && cc.isValid(this.stand.other)) {
            if (!this.stand.detaching) {
                this.node.x += this.stand.other.friction / 100 * this.stand.other.deltaPos.x;
                this.node.y = this.stand.other.node.y + this.stand.other.node.height / 2 + this.node.height / 2;
            }
        }

        this.updateCapsule(dt);
    }
    protected lateUpdate(dt: number): void {
        if (cc.isValid(this.prevPos)) {
            this.deltaPos = cc.v2(this.node.position).sub(this.prevPos);
        }

        this.prevPos = cc.v2(this.node.position);
    }

    getHitByOtherBullet(bullet: Bullet) {
        return this.hits.find(ele => ele.other === bullet);
    }

    canStandOnIt(other: Bullet) {
        return other.isSolid && !other.isSensor;
    }
    isStandOnIt(other: Bullet) {
        return cc.isValid(this.stand) && this.stand.other === other;
    }
    isStandOnYou(other: Bullet) {
        return cc.isValid(other.stand) && other.stand.other === this;
    }
    onEnterStand() {

    }
    onChangeStand() {

    }
    onLeaveStand() {
        if (cc.isValid(this.node)) {
            if (this.state === BulletState.Stand) {
                this.state = BulletState.Physics;
            }
            this.stand = null;
        }
    }
    standOn(bullet: Bullet) {
        let node = new cc.Node("Kp");
        node.groupIndex = this.node.groupIndex;
        node.group = this.node.group;
        let hasOldLand = cc.isValid(this.stand);
        if (hasOldLand) {
            if (this.stand.other !== bullet) {
                this.stand.node.destroy();
                this.stand = null;
            }
        }
        if (!this.stand && cc.isValid(bullet)) {
            this.stand = node.addComponent(BulletStand);
            this.node.addChild(node);
            this.stand.once("leave", this.onLeaveStand, this);
            this.stand.init(this, bullet);
            if (hasOldLand) {
                this.onChangeStand();
            }
            else {
                this.onEnterStand();
            }
        }
        if (this.stand) {
            this.node.y = this.stand.other.node.y + this.stand.other.node.height / 2 + this.node.height / 2 - 1;
            this.state = BulletState.Stand;
        }
        else {
            if (this.state === BulletState.Stand) {
                this.state = BulletState.Physics;
            }
        }
    }

    //被伤害
    damage(dam: DamageType) {
        
    }

    //被举起
    onRaise(other: Bullet, side: 1 | -1) {
    }
    //被取消举起
    onUnraise(other: Bullet, side: 1 | -1) {
    }

    onGrab(other: Bullet) {
        this.state = BulletState.Grabed;
        this.physicsEnabled = false;
        this.hits = [];
        if (this.stand) {
            this.stand.node.destroy();
            this.stand = null;
        }
    }
    onDrop(other: Bullet) {
        this.onUnraise(other, 1);
        this.physicsEnabled = true;
        this.state = BulletState.Physics;
    }
    onUse(other: Bullet) {
        this.emit("on-use", other);
    }
    onUnuse(other: Bullet) {
        this.emit("on-unuse", other);
    }
    applyForce(force: cc.Vec2) {
        this.speed = this.speed.add(force);
    }

    applyForceJump(force: cc.Vec2) {
        if (this.state === BulletState.Stand) {
            if (cc.isValid(this.stand)) {
                this.stand.detaching = true;
            }
        }
        this.applyForce(cc.v2(0, 2000));
    }

    _col: cc.BoxCollider = null;
    @property(cc.BoxCollider)
    get col() {
        if (!cc.isValid(this)) {
            return null;
        }
        if (!this._col) {
            this._col = this.getComponent(cc.BoxCollider);
        }
        return this._col;
    }

    //点是否接近边的任意顶点。
    static FuzzyEqualsV2Arr(v2: cc.Vec2, arr: cc.Vec2[], fuzzyVal: number = 0) {
        for (let i = 0; i < arr.length; i++) {
            if (!v2.fuzzyEquals(arr[i], fuzzyVal)) {
                return false;
            }
        }
        return true;
    }
    static SegmentsIntr(seg1: cc.Vec2[], seg2: cc.Vec2[]) {
        let [a, b] = seg1;
        let [c, d] = seg2;
        /** 1 解线性方程组, 求线段交点. **/
        // 如果分母为0 则平行或共线, 不相交  
        var denominator = (b.y - a.y) * (d.x - c.x) - (a.x - b.x) * (c.y - d.y);
        if (denominator == 0) {
            return false;
        }

        // 线段所在直线的交点坐标 (x , y)      
        var x = ((b.x - a.x) * (d.x - c.x) * (c.y - a.y)
            + (b.y - a.y) * (d.x - c.x) * a.x
            - (d.y - c.y) * (b.x - a.x) * c.x) / denominator;
        var y = -((b.y - a.y) * (d.y - c.y) * (c.x - a.x)
            + (b.x - a.x) * (d.y - c.y) * a.y
            - (d.x - c.x) * (b.y - a.y) * c.y) / denominator;

        /** 2 判断交点是否在两条线段上 **/
        if (
            // 交点在线段1上  
            (x - a.x) * (x - b.x) <= 0 && (y - a.y) * (y - b.y) <= 0
            // 且交点也在线段2上  
            && (x - c.x) * (x - d.x) <= 0 && (y - c.y) * (y - d.y) <= 0
        ) {

            // 返回交点p  
            return cc.v2(x, y);
        }
        //否则不相交  
        return false
    }


    onCollisionEnter(other: cc.BoxCollider, self: cc.BoxCollider) {
    }
    onCollisionExit(other: cc.BoxCollider, self: cc.BoxCollider) {
    }
    onCollisionStay(other: cc.BoxCollider, self: cc.BoxCollider) {
    }
};